package edu.unicauca.opentextbook.controladores;

import edu.unicauca.opentextbook.controladores.util.JsfUtil;
import edu.unicauca.opentextbook.entidades.Objetivo;
import edu.unicauca.opentextbook.entidades.Usuario;
import edu.unicauca.opentextbook.entidades.VotosObjetivo;
import edu.unicauca.opentextbook.fachadas.LibroFacade;
import edu.unicauca.opentextbook.fachadas.ObjetivoFacade;
import edu.unicauca.opentextbook.fachadas.UsuarioFacade;
import edu.unicauca.opentextbook.fachadas.VotosObjetivoFacade;
import edu.unicauca.opentextbook.negocio.VotosObjetivoNegocio;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.primefaces.push.PushContext;
import org.primefaces.push.PushContextFactory;

@ManagedBean(name = "objetivoController")
@ViewScoped
public class ObjetivoController extends BaseController implements Serializable {

    //LOG
    private static final Log LOG = LogFactory
            .getLog(ObjetivoController.class.getName());
    //Controladores
    @ManagedProperty("#{canalesController}")
    private CanalesController canalesController;
    @ManagedProperty("#{loginController}")
    private LoginController loginController;
    //EJBs
    @EJB
    private ObjetivoFacade objetivoFacade;
    @EJB
    private UsuarioFacade usuarioFacade;
    @EJB
    private LibroFacade libroFacade;
    @EJB
    private VotosObjetivoFacade votosObjetivoFacade;
    //Objetos
    private Objetivo objetivo;
    //Listas
    private List<Objetivo> listaObjetivos;
    private List<VotosObjetivo> listaVotosObjetivos;
    private List<Objetivo> listaObjetivosPorCubrir;
    //Variables
    private String canal;

    //Metodos
    @PostConstruct
    public void init() {
        LOG.info("Dentro de init ObjetivoController...");
        objetivo = new Objetivo();
        listaVotosObjetivos = new ArrayList<VotosObjetivo>();
        obtenerParametro();
        consultarObjetivos();
        cargarObjetivosPorCubrir();
        registrarCanal();
    }

    private void obtenerParametro() {
        final FacesContext facesContext = FacesContext.getCurrentInstance();
        final Map params = facesContext.getExternalContext().getRequestParameterMap();
        final int idLibro = Integer.parseInt(params.get("libro").toString());

        objetivo.setLibro(libroFacade.buscarLibro(idLibro));

    }

    public void consultarObjetivos() {
        LOG.info("Dentro de consultarObjetivos...");
        listaObjetivos = objetivoFacade.consultarObjetivosPorLibro(objetivo.getLibro().getIdLibro());
    }

    public void registrarObjetivo() {
        final Usuario usuario = usuarioFacade.buscarUsuarioPorNick(loginController.getNick());

        objetivo.setUsuario(usuario);
        objetivo.setFecha(new Date());

        try {
            userTransaction.begin();

            objetivoFacade.create(clonarObjeto(objetivo));
            commitTransaction();

            comunicarseConCanalDeLibro(objetivo.getLibro().getNombre());

            objetivo.setDescripcion(null);

        } catch (Exception ex) {
            anadirMensajeError("Error al registar");
        }
    }

    private void registrarCanal() {
        LOG.info("Dentro de registrarCanal: " + objetivo.getLibro().getNombre());
        final String libro = objetivo.getLibro().getNombre();
        final String canalAux = canalesController.obtnereCanalPorLibro(libro);

        canal = "/" + libro;

        if (canalAux == null) {
            canalesController.adicionarCanalParaLibro(libro, canal);
        }
    }

    private void comunicarseConCanalDeLibro(String libro) {
        LOG.info("Dentro de comunicarseConCanalDeLibro...");
        final String canalLibro = canalesController.obtnereCanalPorLibro(libro);
        LOG.info("El canal es: " + canalLibro);

        if (canalLibro != null && !canalLibro.isEmpty()) {
//            final EventBus eventBus = EventBusFactory.getDefault().eventBus();
//            eventBus.publish(canalLibro, null);
            PushContext pushContext = PushContextFactory.getDefault().getPushContext();
            pushContext.push(canalLibro, null);
        }
    }

    public void cargarObjetivosParaVotar() {
        LOG.info("Dentro de cargarObjetivosParaVotar...");
        final Usuario usuario = usuarioFacade.buscarUsuarioPorNick(loginController.getNick());

        listaVotosObjetivos = votosObjetivoFacade.cargarVotosPorUsuarioYlibro(usuario.getIdUsuario(), objetivo.getLibro().getIdLibro());

        for (Objetivo obj : listaObjetivos) {
            if (!VotosObjetivoNegocio.validarExistenciaEnLista(listaVotosObjetivos, obj)) {
                final VotosObjetivo votosObjetivo = new VotosObjetivo();

                votosObjetivo.setObjetivo(obj);
                votosObjetivo.setUsuario(usuario);

                listaVotosObjetivos.add(votosObjetivo);
            }
        }
    }

    public void realizarVotacion() {
        LOG.info("Dentro de realizarVotacion...");
        try {
            userTransaction.begin();

            for (VotosObjetivo votosObjetivo : listaVotosObjetivos) {
                if (votosObjetivo.getIdVotosObjetivo() == 0 && votosObjetivo.getValor() != null) {
                    votosObjetivoFacade.create(votosObjetivo);
                }
            }
            commitTransaction();

            anadirMensajeInfo("Votación exitosa");

            listaVotosObjetivos.clear();

            JsfUtil.ejecutarJavaScript("wvVotacionObjetivos.hide();");

        } catch (Exception ex) {
            anadirMensajeError("Error en el proceso de votación");
        }
    }

    public void cargarObjetivosPorCubrir() {
        LOG.info("Dentro de cargarObjetivosPorCubrir...");

        listaObjetivosPorCubrir = votosObjetivoFacade.cargarVotosPorCubrir(objetivo.getLibro().getIdLibro());
    }

    //Getters y Setters
    public Objetivo getObjetivo() {
        return objetivo;
    }

    public void setObjetivo(Objetivo objetivo) {
        this.objetivo = objetivo;
    }

    public List<Objetivo> getListaObjetivos() {
        return listaObjetivos;
    }

    public void setListaObjetivos(List<Objetivo> listaObjetivos) {
        this.listaObjetivos = listaObjetivos;
    }

    public CanalesController getCanalesController() {
        return canalesController;
    }

    public void setCanalesController(CanalesController canalesController) {
        this.canalesController = canalesController;
    }

    public String getCanal() {
        return canal;
    }

    public void setCanal(String canal) {
        this.canal = canal;
    }

    public LoginController getLoginController() {
        return loginController;
    }

    public void setLoginController(LoginController loginController) {
        this.loginController = loginController;
    }

    public List<VotosObjetivo> getListaVotosObjetivos() {
        return listaVotosObjetivos;
    }

    public void setListaVotosObjetivos(List<VotosObjetivo> listaVotosObjetivos) {
        this.listaVotosObjetivos = listaVotosObjetivos;
    }

    public List<Objetivo> getListaObjetivosPorCubrir() {
        return listaObjetivosPorCubrir;
    }

    public void setListaObjetivosPorCubrir(List<Objetivo> listaObjetivosPorCubrir) {
        this.listaObjetivosPorCubrir = listaObjetivosPorCubrir;
    }

    //Clonar
    private Objetivo clonarObjeto(Objetivo objeto) {
        Objetivo objetoClonado = null;
        if (objeto == null) {
            return null;
        } else {
            try {
                objetoClonado = (Objetivo) SerializationUtils.clone(objeto);
            } catch (Exception e) {
                objetoClonado = null;
            } finally {
                return objetoClonado;
            }
        }
    }

    //Converter
    @FacesConverter(forClass = Objetivo.class)
    public static class ObjetivoControllerConverter implements Converter {

        public Object getAsObject(FacesContext facesContext, UIComponent component, String value) {
            if (value == null || value.length() == 0) {
                return null;
            }
            ObjetivoController controller = (ObjetivoController) facesContext.getApplication().getELResolver().
                    getValue(facesContext.getELContext(), null, "objetivoController");
            return controller.objetivoFacade.find(getKey(value));
        }

        int getKey(String value) {
            int key;
            key = Integer.parseInt(value);
            return key;
        }

        String getStringKey(int value) {
            StringBuffer sb = new StringBuffer();
            sb.append(value);
            return sb.toString();
        }

        public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
            if (object == null) {
                return null;
            }
            if (object instanceof Objetivo) {
                Objetivo o = (Objetivo) object;
                return getStringKey(o.getIdObjetivo());
            } else {
                throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName() + "; expected type: " + Objetivo.class.getName());
            }
        }
    }
}
